/*
 *
 *  *    Copyright 2020-2021 luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.boot.webflux.filter;


import com.luter.heimdall.core.config.ConfigManager;
import com.luter.heimdall.core.config.HeimdallProperties;
import com.luter.heimdall.core.fuction.AbcVoidFunction;
import com.luter.heimdall.core.manager.AuthorizationManager;
import com.luter.heimdall.core.token.SimpleToken;
import com.luter.heimdall.core.utils.PathUtil;
import com.luter.heimdall.webflux.function.AbcMonoVoidFunction;
import org.slf4j.Logger;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.lang.NonNull;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

import java.util.List;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * 认证授权过滤器
 *
 * @author luter
 */
public class HeimdallAuthorizeFilter implements WebFilter {
    /**
     * The constant log.
     */
    private static final Logger log = getLogger(HeimdallAuthorizeFilter.class);
    /**
     * 默认排除常见静态资源
     * <p>
     * "/static/**",
     * <p>
     * "/images/**",
     * <p>
     * "/css/**",
     * <p>
     * "/js/**",
     * <p>
     * "/favicon*"
     */
    public static final String[] DEFAULT_STATIC_RESOURCE = new String[]{
            "/static/**",
            "/images/**",
            "/css/**",
            "/js/**",
            "/favicon*",
            "/error*",
    };
    /**
     * 认证授权管理器
     */
    private AuthorizationManager authorizationManager;

    /**
     * The constant PATH_UTIL.
     */
    private static final PathUtil PATH_UTIL = new PathUtil();
    /**
     * 授权成功后处理
     */
    private AbcMonoVoidFunction<ServerWebExchange, WebFilterChain, SimpleToken> success;
    /**
     * The Error.
     */
    private AbcVoidFunction<ServerWebExchange, WebFilterChain, Throwable> error;

    /**
     * 创建过滤器
     *
     * @param authorizationManager the auth manager
     */
    public HeimdallAuthorizeFilter(AuthorizationManager authorizationManager) {
        this.authorizationManager = authorizationManager;
    }

    @Override
    public @NonNull
    Mono<Void> filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (request.getMethodValue().equalsIgnoreCase(HttpMethod.OPTIONS.name())) {
            //options 请求全部放行
            return chain.filter(exchange);
        }
        final String method = request.getMethodValue();
        final String uri = request.getURI().getPath();
        final HeimdallProperties config = ConfigManager.getConfig();
        final boolean enabled = config.getAuthority().isEnabled();
        final List<String> includePatterns = config.getAuthority().getIncludes();
        final List<String> excludePatterns = config.getAuthority().getExcludes();
        try {
            //在拦截规则里，但是不在排除规则中的，会被鉴权
            if (enabled && PATH_UTIL.isMatch(includePatterns, uri) && !PATH_UTIL.isMatch(excludePatterns, uri)) {
                log.info("[AuthWebFluxFilter.filter]::Authorization rules matched. uri= [{}]", uri);
                final SimpleToken currentToken = authorizationManager
                        .getAuthenticationManager().getCurrentToken(true);
                authorizationManager.isAuthorized(currentToken.getDetails(), method, uri, true);
                if (null != success) {
                    success.accept(exchange, chain, currentToken);
                }
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            if (null != error) {
                error.accept(exchange, chain, throwable);
            } else {
                throw throwable;
            }
        }
        log.info("[AuthWebFluxFilter.filter]:: No authorization rule matched. uri= [{}]", uri);
        return chain.filter(exchange).doFinally(r -> {
            log.info("[filter]doFinally Clear all Context. SignalType = [{}], RequestId: = [{}]", r.name(), request.getId());
        });
    }

    /**
     * Gets authorization manager.
     *
     * @return the authorization manager
     */
    public AuthorizationManager getAuthorizationManager() {
        return authorizationManager;
    }

    /**
     * Sets authorization manager.
     *
     * @param authorizationManager the authorization manager
     * @return the authorization manager
     */
    public HeimdallAuthorizeFilter setAuthorizationManager(AuthorizationManager authorizationManager) {
        this.authorizationManager = authorizationManager;
        return this;
    }

    /**
     * On success auth way web flux filter.
     *
     * @param success the success
     * @return the auth way web flux filter
     */
    public HeimdallAuthorizeFilter onSuccess(AbcMonoVoidFunction<ServerWebExchange, WebFilterChain, SimpleToken> success) {
        this.success = success;
        return this;
    }

    /**
     * On error auth way web flux filter.
     *
     * @param error the error
     * @return the auth way web flux filter
     */
    public HeimdallAuthorizeFilter onError(AbcVoidFunction<ServerWebExchange, WebFilterChain, Throwable> error) {
        this.error = error;
        return this;
    }
}
